package com.ymt.bpm.engine.handler.activity.impl;


import com.ymt.bpm.dao.MDao;
import com.ymt.bpm.dao.Mapper;
import com.ymt.bpm.engine.handler.activity.AsyncServiceTaskDelegate;
import com.ymt.bpm.model.PJob;
import com.ymt.bpm.model.PSvctaskTodoWithBLOBs;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import sun.management.VMManagement;

import java.lang.management.ManagementFactory;
import java.lang.management.RuntimeMXBean;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.List;

import static java.lang.Thread.sleep;

/**
 * Created by Johnny on 2017/6/21.
 */
@Component
public class AsyncServiceTaskExecutor {

    private Logger log = LoggerFactory.getLogger(this.getClass());
    private static final String JOB_ID = "AsyncServiceTaskDelegate.run";

    @Autowired
    private MDao dao;

    @Autowired
    private AsyncServiceTaskDelegate astd;

    private String pid = getHost()+"@"+getPid();

    private int timeout = 180;

    private PJob job;   //当前任务

    @Scheduled(cron="*/5 * * * * ?")
    public void executeJob() {
        log.trace("trying Job: AsyncServiceTaskDelegate.run on pid=" + pid);
        if (this.job==null) {
            PJob job = dao.selectOne(Mapper.PJobMapper, "selectByPrimaryKey", JOB_ID);
            if (job==null) {
                //没有job，新建
                job = new PJob();
                job.setJobId(JOB_ID);
                job.setPid(pid);
                job.setStatus(PJob.STATUS_ACTIVE);
                dao.insert(Mapper.PJobMapper, "insertNew", job);
                if (log.isTraceEnabled()) {
                    log.trace("Job created and aquired: AsyncServiceTaskDelegate.run, on pid="+pid);
                }
                this.job = job;
                runTask();
            } else {
                if (!pid.equalsIgnoreCase(job.getPid())) {  //在其他进程执行中
                    if (job.getDelta()>timeout) {   //超时则接管任务
                        String oldpid = job.getPid();
                        job.setPid(pid);
                        job.setStatus(PJob.STATUS_ACTIVE);
                        dao.update(Mapper.PJobMapper, "takeJob", job);
                        if (log.isTraceEnabled()) {
                            log.trace("Job taken over and aquired: AsyncServiceTaskDelegate.run, on new pid="+pid+" from old pid="+oldpid);
                        }
                        this.job = job;
                        runTask();
                    }
                }
            }
        } else {
            if (PJob.STATUS_ACTIVE.equalsIgnoreCase(this.job.getStatus())) {
                //正在执行，什么都不做
            } else {
                //执行新任务
                this.job.setStatus(PJob.STATUS_ACTIVE);
                dao.update(Mapper.PJobMapper, "takeJob", this.job);
                if (log.isTraceEnabled()) {
                    log.trace("Job restarted: AsyncServiceTaskDelegate.run, on pid="+pid);
                }
                runTask();
            }
        }
    }

    private String getHost() {
        String name = ManagementFactory.getRuntimeMXBean().getName();
        String pid = name.split("@")[1];
        return pid;
    }

    private int getPid() {
        try {
            RuntimeMXBean runtime = ManagementFactory.getRuntimeMXBean();
            Field jvm = runtime.getClass().getDeclaredField("jvm");
            jvm.setAccessible(true);
            VMManagement mgmt = (VMManagement) jvm.get(runtime);
            Method pidMethod = mgmt.getClass().getDeclaredMethod("getProcessId");
            pidMethod.setAccessible(true);
            int pid = (Integer) pidMethod.invoke(mgmt);
            return pid;
        } catch (Exception e) {
            return -1;
        }
    }

    private void runTask() {
        if (log.isTraceEnabled()) {
            log.trace("runTask job executing...");
        }
        List<PSvctaskTodoWithBLOBs> list = getUnsent();
        if (list!=null) {
            for (PSvctaskTodoWithBLOBs sttd : list) {
                try {
                    astd.run(sttd);
                } catch (Exception e) {
                    e.printStackTrace();
                    log.warn("run servicetask failed:"+sttd.getId()+", activityId="+sttd.getActivityId()+", activityName="+sttd.getActivityName()+" :"+e.getMessage());
                }
            }
        }
        //mark job as finished
        this.job.setStatus(PJob.STATUS_IDLE);
        dao.update(Mapper.PJobMapper, "takeJob", this.job);
    }

    private List<PSvctaskTodoWithBLOBs> getUnsent() {
        try {
            List<PSvctaskTodoWithBLOBs> list = dao.selectObjListByObj(Mapper.PSvctaskTodoMapper, "getUnsent", 10);
            return list;
        } catch (Exception e) {
            e.printStackTrace();
            log.warn("getting servicetask failed, "+e.getMessage());
            return null;
        }
    }

}
